home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Programmer Disk
/
The Programmer Disk (Microforum).iso
/
xpro
/
pascal4
/
pro11
/
errtrace.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1988-09-16
|
11KB
|
255 lines
{
ErrTrace - a unit for TurboPascal v4.0 to display an error traceback
information.
Author : Michal Jankowski <sieminski@rzsin.sin.ch>
<sieminski%rzsin.sin.ch@cernvax> (Bitnet)
Version : 1.0 16.09.1988
}
{$N- No floating point needed }
{$B- Boolean short circuit }
{$R- No range checking needed }
{$D- No debug information }
unit ErrTrace;
{-----------------------------------------------------------------------------}
interface
const
Continue : boolean = false;
{-----------------------------------------------------------------------------}
implementation
{
convert integer to hex
}
type
hexWord = string[4];
function hex( x : word ) : hexWord;
const hexDigit : array [0..15] of char = '0123456789ABCDEF';
begin
hex := hexDigit[(x shr 12) and $f ] +
hexDigit[(x shr 8) and $f ] +
hexDigit[(x shr 4) and $f ] +
hexDigit[ x and $f ];
end; { function hex }
const
FarCallOpcode : byte = $9A;
NearCallOpcode : byte = $E8;
PushbpOpcode : byte = $55;
MovbpspOpcode : word = $E589;
var
NF : char; { 'N' for near, 'F' for far calls }
Errorcs,
Errorip,
newip,
adr,
newcs,
_cs,
_ip,
_bp : word;
first,
found : boolean;
ExitSave : pointer;
type
ErrorMsg = record
ErrTxt : String[32]; { Longest message has 32 characters }
ErrNo : integer;
end;
const
ErrorMsgSize = 26; { Number of messages }
ErrorMsgs : array[1..ErrorMsgSize] of ErrorMsg = (
{ Run-time error messages }
( ErrTxt: 'Division by zero'; ErrNo: 200 ),
( ErrTxt: 'Range check error'; ErrNo: 201),
( ErrTxt: 'Stack overflow error'; ErrNo: 202),
( ErrTxt: 'Heap overflow error'; ErrNo: 203),
( ErrTxt: 'Invalid pointer operation'; ErrNo: 204),
( ErrTxt: 'Floating point overflow'; ErrNo: 205),
( ErrTxt: 'Floating point underflow'; ErrNo: 206),
( ErrTxt: 'Invalid floating point operation'; ErrNo: 207),
{ I/O error messages }
( ErrTxt: 'File not found'; ErrNo: 2),
( ErrTxt: 'Path not found'; ErrNo: 3),
( ErrTxt: 'Too many open files'; ErrNo: 4),
( ErrTxt: 'File access denied'; ErrNo: 5),
( ErrTxt: 'Invalid file handle'; ErrNo: 6),
( ErrTxt: 'Invalid file access code'; ErrNo: 12),
( ErrTxt: 'Invalid drive number'; ErrNo: 15),
( ErrTxt: 'Cannot remove current directory'; ErrNo: 16),
( ErrTxt: 'Cannot rename across drives'; ErrNo: 17),
( ErrTxt: 'Disk read error'; ErrNo: 100),
( ErrTxt: 'File not open'; ErrNo: 103),
( ErrTxt: 'File not open for input'; ErrNo: 104),
( ErrTxt: 'File not open for output'; ErrNo: 105),
( ErrTxt: 'Invalid numeric format'; ErrNo: 106),
( ErrTxt: 'Disk write error'; ErrNo: 101),
( ErrTxt: 'File not assigned'; ErrNo: 102),
( ErrTxt: 'Drive not ready'; ErrNo: 152),
( ErrTxt: 'Unknown Error'; ErrNo: 0));
{$f+}
procedure ErrorTrap;
{$f-}
var
i : integer; { Index to table of messages, also used to }
{ the stack, must be the FIRST local variable }
begin
if (ExitCode<>0) { only on error exits }
and (ExitCode<>255) then begin { not on user break }
{ Look for error number in table }
i := 0;
repeat
i := i+1;
until
(ErrorMsgs[i].ErrNo = ExitCode) { found }
or (i = ErrorMsgSize); { use 'Unknown error' message }
{ Now look for traceback information }
{ i is the first local variable, use it to find local stack }
adr := ofs(i)+2; { Get offset of bottom of our stack }
_bp := memw[sseg:adr]; { Get old bp from stack }
found := false;
Errorcs := Seg(ErrorAddr^)+PrefixSeg+$10;
{ Convert relative segment to absolute }
Errorip := Ofs(ErrorAddr^);
_cs := Errorcs;
_ip := Errorip;
{ Look for far call to error-check routine - 'normal' errors }
if (mem[_cs:_ip-5]= FarCallOpcode) { Found far call }
{ It should be : far call to error-check routine, }
{ then from it far call to our procedure }
{ Compare segments }
and (memw[_cs:_ip-2] = memw[sseg:adr+4])
{ Offsets differ by less than $80 - assume that was a call from }
{ error-check routine }
and (abs(integer(memw[_cs:_ip-4]-memw[sseg:adr+2]))<$80) then begin
found := true;
end;
{ Not found, so it must be arithmetic (80x87) error }
if not found then begin
{ First look for errors in initialization part of unit }
{ Units have special entry sequence - no 'push bp' instruction }
_ip := memw[sseg:_bp]; { Get return address from stack }
_cs := memw[sseg:_bp+2];
{ First look for far call to erroneous routine }
if (mem[_cs:_ip-5]=FarCallOpCode) then begin
newip := memw[_cs:_ip-4];
_cs := memw[_cs:_ip-2];
{ Look for special entry sequence }
if (memw[_cs:newip]=MovbpspOpcode) then begin
found := true;
end;
end;
end;
if not found then begin
{ Now look for errors in procedure reached by far call }
_ip := memw[sseg:_bp+2]; { Get return address from stack (skip old bp) }
_cs := memw[sseg:_bp+4];
{ First look for far call to erroneous routine }
if (mem[_cs:_ip-5]=FarCallOpcode) then begin
newip := memw[_cs:_ip-4];
_cs := memw[_cs:_ip-2];
{ Look for standard entry sequence }
if (mem[_cs:newip]=PushbpOpcode) and (memw[_cs:newip+1]=MovbpspOpcode) then begin
found := true;
end;
end;
end;
if not found then
{ Now look for errors in procedure reached by near call }
{ This is tricky, because we don't know cs at the time of error - }
{ ErrorAdr gives only 'normalized pointer'. But it was pushed on stack }
{ somewhere by the actual 80x87 interrupt, so... }
repeat { look for old cs on stack }
_cs := memw[sseg:adr]; { try next word from stack for cs }
{ ip is already taken from stack }
if mem[_cs:_ip-3]=NearCallOpcode then begin
newip := _ip+memw[_cs:_ip-2]; { Near calls are relative }
{ Look for standard entry sequence }
if (mem[_cs:newip]=PushbpOpcode) and (memw[_cs:newip+1]=MovbpspOpcode) then begin
found := true;
end;
end;
inc(adr,2); { point to next word on stack }
until found or (adr>_bp);{ stop when stack ends }
if not found then begin
{ Nothing found on stack, so assume main program }
_cs := PrefixSeg+$10;
found := true; { Always true! }
end;
if found then begin
{ For 8087 errors, ErrorAdr is a 'normalized' pointer, so convert it }
inc(Errorip,$10*(Errorcs-_cs));
Errorcs := _cs;
end;
{ Write message, use relative segment }
writeln('Runtime error ',ExitCode,' at ',hex(Errorcs-PrefixSeg-$10),':',
hex(Errorip));
writeln(ErrorMsgs[i].ErrTxt);
if found then begin
first := true;
{ Now loop thru traceback ... }
repeat
found := false;
_ip := memw[sseg:_bp+2]-3; { point to assumed 'call' instruction }
{ try near call }
if mem[_cs:_ip]=NearCallOpcode then begin
newip := _ip+3+memw[_cs:_ip+1];
{ Look for standard entry sequence }
if (mem[_cs:newip]=PushbpOpcode) and (memw[_cs:newip+1]=MovbpspOpcode) then begin
newcs := _cs;
found := true;
NF := 'N';
end;
end;
if not found then begin
_ip := _ip-2; { Adjust for far call }
newcs := memw[sseg:_bp+4]; { Get cs }
if mem[newcs:_ip]=FarCallOpcode then begin
{ It should be call to cs at previous level, so check it }
if (memw[newcs:_ip+3] = _cs) then begin
_cs := newcs;
newip := memw[_cs:_ip+1];
newcs := memw[_cs:_ip+3];
{ Look for standard entry sequence }
if (mem[newcs:newip]=PushbpOpcode) and (memw[newcs:newip+1]=MovbpspOpcode) then begin
found := true;
NF := 'F';
end;
end;
end;
end;
if found then begin
if first then begin
{ Here on first pass, but only if there is anything to print }
writeln('Traceback');
first := false;
end;
{ Write message, use relative segments again }
writeln(NF,' Procedure at ',hex(newcs-PrefixSeg-$10),':',hex(newip),
' Called from ',hex(_cs-PrefixSeg-$10),':',hex(_ip));
_bp := memw[sseg:_bp];
end;
until not found;
end;
if not Continue then
halt(ExitCode); { Halt program }
end; { if ExitCode<>0 }
{ On normal exit, or if Continue = true, proceed to next ExitProc in chain }
ExitProc := ExitSave;
end; { ErrorTrap }
begin
ExitSave := ExitProc; { Save old pointer }
ExitProc := @ErrorTrap; { Install our procedure }
end.